
#import "MyObject.h"
#import "Term.h"
#import "Modem.h"
#import "Fdset.h"
#import "Escaper.h"
#import "StringResponder.h"

void fdHandler(int fd, void *selfp)
{
    id self = selfp;
    [self getLine];
}

int normal_key(int c, id escaper)
{
  static escape='~';
  static esc_flag=0;

  if (esc_flag==0) {
    if (c==escape) {
      esc_flag=1;
      return(0);
    }
    else {
      return(1);
    }
  }
  else {  /* esc_flag == 1  ..handle escaped character.. */
    esc_flag=0;
    if (c==escape) {     /* send escape character if two in a row */
      return(1);
    }
    [escaper doEscape:c];
    return(0);
  }
}

@implementation MyObject

- init;
{
//    int seteuid(uid_t);
  
    [super init];
    seteuid(getuid());
    term = [Term new];
    escaper = [Escaper newEscaperWithTerm:term andModem:modem];
    fdset = [Fdset new];
    if (term==nil || fdset==nil || escaper==nil)
	NXRunAlertPanel(NULL, "Problem initialising", NULL, NULL, NULL);
    /* change terminal to raw mode */
    [[[term setRaw] unSetEcho] unSetCrmod];
    sr = [StringResponder new];
    [sr addString:"CONNECT!" returnValue:connected];
    [sr addString:"CONNECT 1200" returnValue:connect1200];
    [sr addString:"CONNECT 2400" returnValue:connect2400];
    [sr addString:"CONNECT 9600" returnValue:connect9600];
    [sr addString:"NO CARRIER!" returnValue:no_carrier];
    [sr addString:"BUSY!" returnValue:busy];
    [sr addString:"AT" returnValue:modem_command];
    [sr addString:"at" returnValue:modem_command];
    
// put back in later?
//    if (argc>1) {
//	[escaper readScript:argv[1]];
//    }

    return self;
}

- free;
{
    if (fdHandlerInstalled) {
	DPSRemoveFD(mfd);
	if (mfd) close(mfd);
    }
    return [super free];
}

//
//	delegate methods
//

- awakeFromNib;
{
    static NXDefaultsVector tipDefaults = {
	{"DebugLevel", "0"},
	{"Speed", "9600"},
	{"Parity", "NONE"},
	{"Dial", ""},
	{"Init", ""},
	{"Port", "2"},
	{"Flow", "NO"},
	{"Capture", ""},
	{"SendProtocol", "Xmodem"},
	{"ReceiveProtocol", "Xmodem"},
	{NULL}
    };
    int	num;
    char *string;
    
    NXRegisterDefaults("IBTip", tipDefaults);
    sscanf(NXGetDefaultValue("IBTip", "DebugLevel"), "%d", &debug);
    [self resetConfig:self];
    
    sscanf(NXGetDefaultValue("IBTip", "Speed"), "%d", &num);
    if (strcmp(NXGetDefaultValue("IBTip", "Port"), "1") == 0)
	if (strcmp(NXGetDefaultValue("IBTip", "Flow"), "YES") == 0)
	    string = "/dev/cufa";
	else
	    string = "/dev/cua";
    else
	if (strcmp(NXGetDefaultValue("IBTip", "Flow"), "YES") == 0)
	    string = "/dev/cufb";
	else
	    string = "/dev/cub";
    modem = [Modem newPort:string speed:num];
    if (modem) {
	mfd = [modem getfd];
	[fdset addfd:mfd];  // listen on the modem
	
	DPSAddFD(mfd, fdHandler, self, NX_RUNMODALTHRESHOLD);
	fdHandlerInstalled = YES;
	if (strcmp(NXGetDefaultValue("IBTip", "Init"), "") != 0) {
	    [modem writeOut:NXGetDefaultValue("IBTip", "Init")];
	    [modem writeOut:"\r"];
	}
    } else {
	NXRunAlertPanel(NULL, "Unable to open port %s", NULL, NULL, NULL, string);
	[[output window] orderOut:self];
    }

    connectSound = [Sound findSoundFor:"Connected"];

    return self;
}

- windowDidBecomeMain:sender;
{
    [input selectText:self];
    return self;
}

//
//	action methods
//

- captureFile:sender
{
    id	savePanel = [SavePanel new];
    
    if ([savePanel runModal] == NX_OKTAG) {
	[captureFile setStringValue:[savePanel filename]];
	[captureFileWell setStringValue:[savePanel filename]];
    }
    return self;
}

- configure:sender
{
    char string[80];
    
    if ([flow state])
	NXWriteDefault("IBTip", "Flow", "YES");
    else
	NXWriteDefault("IBTip", "Flow", "NO");
    sprintf(string, "%d", [port selectedTag]);
    if (NXGetDefaultValue("IBTip", "Port") != string)
	NXWriteDefault("IBTip", "Port", string);
    sprintf(string, "%d", [[[speedPopup target] itemList] selectedTag]);
    if (strcmp(NXGetDefaultValue("IBTip", "Speed"), string) != 0)
	NXWriteDefault("IBTip", "Speed", string);
    if (NXGetDefaultValue("IBTip", "Dial") != [dialString stringValue])
	NXWriteDefault("IBTip", "Dial", [dialString stringValue]);
    if (NXGetDefaultValue("IBTip", "Init") != [initString stringValue])
	NXWriteDefault("IBTip", "Init", [initString stringValue]);
    if (NXGetDefaultValue("IBTip", "Capture") != [captureFile stringValue])
	NXWriteDefault("IBTip", "Capture", [captureFile stringValue]);
    return self;
}

- dial:sender;
{
    char temp[200];
    char dialPrefix[]="ATDT", dialPostfix[]="\r"; 
    // constants for now; later local var's of modem
    
    sprintf(temp,"%s%s%s\n",dialPrefix, [dialString stringValue], dialPostfix);
    [modem writeOut:temp];
    dialing = TRUE;
    return self;
}

- hangup:sender
{
    [[term pushState] reset];
    [modem hangup];
    dialing = FALSE;
    [term popState];
    return self;
}

- receiveFile:sender
{
    [[term pushState] reset];
    [modem receive];
    [term popState];
    return self;
}

- reset:sender
{
    [term reset];
    return self;
}

- resetConfig:sender
{
//	{"Parity", "NONE"},
//	{"SendProtocol", "Xmodem"},
//	{"ReceiveProtocol", "Xmodem"},
    
    int num;
    
    if (strcmp(NXGetDefaultValue("IBTip", "Flow"), "YES") == 0)
	[flow setState:1];
    else
	[flow setState:0];
    sscanf(NXGetDefaultValue("IBTip", "Port"), "%d", &num);
    [port selectCellWithTag:num];
//    sscanf(NXGetDefaultValue("IBTip", "Speed", "%d", &num);
//    [[speedPopup target] selectCellWithTag:num];
    [speedPopup setTitle:NXGetDefaultValue("IBTip", "Speed")];
    [dialString setStringValue:NXGetDefaultValue("IBTip", "Dial")];
    [initString setStringValue:NXGetDefaultValue("IBTip", "Init")];
    [captureFile setStringValue:NXGetDefaultValue("IBTip", "Capture")];
    [captureFileWell setStringValue:NXGetDefaultValue("IBTip", "Capture")];
    return self;
}

- runScript:sender;
{
    id openPanel = [OpenPanel new];
    FILE *script;
    char buf[MAXPATHLEN];

    if ([openPanel runModal] == NX_OKTAG) {
	if ((script = fopen([openPanel filename], "r")) == NULL) {
	    NXRunAlertPanel(NULL, "Can't read script", NULL, NULL, NULL);
	    return self;
	}
	while (feof(script) == 0) {
	    fgets(buf, MAXPATHLEN, script);
	    buf[strlen(buf)-1] = '\r';
	    [modem writeOut:buf];
	    sleep(2);
	}
	fclose(script);
    }
    return self;
}

- sendFile:sender
{
    id openPanel = [OpenPanel new];

    if ([openPanel runModal] == NX_OKTAG) {
	[[term pushState] reset];
	[modem sendFile:[openPanel filename]];
	[term popState];
    }
    return self;
}

- sendLine:sender;
{
    if(normal_key([input stringValue][0],escaper)) {
	[modem writeOut:[input stringValue]];
	[modem writeOut:"\r"];
    }
    [input setStringValue:""];
    [input selectText:self];
    return self;
}

/* check for zmodem invitation to download -- the ZRQINIT frame --
 * else write characters to screen.
 */

#define ZDLE 030

- getLine;
{
    char buf[16384];
    int nfound;
    static char ZRQINITseq[] =  "~B0";  // the first char will be ZDLE
//    static char cr='\r';
    int i, chars;
    char ch;
    enum responses seen;

    nfound = [fdset waitOnInputFor:0];
    if (nfound < 0) {
	[term reset];
	perror("Fdset:waitOnInputFor had an error\n");
	NXRunAlertPanel(NULL, "Fdset:waitOnInputFor had an error", NULL, NULL, NULL);
	DPSRemoveFD(mfd);	/* end of file */
	[NXApp terminate:self];
    }
    while (nfound--) {
	if ([fdset getReadyfd] == mfd) {
	    if (!trigger) { // initialize trigger
		trigger = [StringResponder new];
		ZRQINITseq[0] = ZDLE;
		[trigger addString:ZRQINITseq returnValue:1];
	    }
	
	    chars = [modem readInto:buf];
	    if ([trigger inputString:buf]) {
		[modem receiveZmodem];
	    } else {
		for (i=0;i<chars;i++) {
		    ch = buf[i] &= 0x7f; //mask any parity bit
	    //    if (ch == '\n')
	    //      write(1,&cr,1);
		}
	    }
	}
    }
    
    if (dialing) {
	for (i=0; i < strlen(buf); i++) {  // convert end of line chars to !
	    if (buf[i] == '\r' || buf[i] == '\n') buf[i]='!';
	}
	seen = [sr inputString:buf];
	dialing = FALSE;
	switch (seen) {
	case connected:
	    [connectSound play];
	    break;
	case connect1200:
	    [modem setSpeed:1200];
	    [connectSound play];
	    break;
	case connect2400:
	    [modem setSpeed:2400];
	    [connectSound play];
	    break;
	case connect9600:
	    [modem setSpeed:9600];
	    [connectSound play];
	    break;
	case no_carrier:
	    [self output:"modem detected no carrier, trying again...\n"];
	    [self dial:self];
	    break;
	case busy:
	    [self output:"busy, trying again...\n"];
	    sleep(10);
	    [self dial:self];
	    break;
	case no_dialtone:
	    [self output:"modem couldn't get dialtone; check phone\n"];
	    break;
	case user_abort:
	    [self output:"User aborted dialing\n"];
	    break;
	case modem_command:
	    dialing = TRUE;	// still, check next line
	    break;
	}
    } else {
	[self output:buf];
    }
    [input selectText:self];
    return self;
}

- toggleCapture:sender
{
    if ([modem isCapturing]) {
	[captureMenu setTitle:"Capture"];
	[modem endCapture];
    } else {
	[captureMenu setTitle:"End Capture"];
	if (strcmp([captureFile stringValue], "") == 0)
	    [modem captureSessionInFile:"tip.record"];
	else
	    [modem captureSessionInFile:[captureFile stringValue]];
    }
    return self;
}

- output:(char *)string;
{
    [output setSel:[output textLength] :0];
    [output replaceSel:string];
    [output scrollSelToVisible];
    return self;
}

@end
